package com.uday.frmwrk.filesystem
{
	import com.uday.kraterchat.chatclient.data.FileDataItem;
	
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	import mx.collections.SortField;

	public class ChatFileStream extends FileStream
	{
		public function ChatFileStream(file:File = null,fileId:Number = 0)
		{
			super();
			if(fileId == 0){
				_fileId = ChatFileStream.numSeqIndex++;
			} else{
				_fileId = fileId;
			}
			
			if(file != null){
				this.file = file;
				if(this.file.exists){
					this.numFileSize = this.file.size;
				}
			}
		}
		private static var numSeqIndex:Number = 1000;
		
		public var file:File = null;
		private var dataArr:ByteArray = new ByteArray();
		private var _fileId:Number;
		public var trnsfrId:Number;
		private var buffer:ArrayCollection;
		private var fileMode:String;
		public var lstReadPos:ArrayCollection = new ArrayCollection();
		public var lstRecvdPos:ArrayCollection = new ArrayCollection();
		private var waitForPcktTimer:Timer = null;
		public var numFileSize:Number = 0;
		
		public function get fileId():Number{
			return _fileId;
		}
		
		public function openAsyncMode(fileMode:String,numFileSize:Number = 0):void{
			if((fileMode == FileMode.APPEND) || (fileMode == FileMode.WRITE) || (fileMode == FileMode.UPDATE)){
				openFileForWriting();
				this.numFileSize = numFileSize;
				waitForPcktTimer.addEventListener(TimerEvent.TIMER_COMPLETE,waitComplete);
			}
			this.fileMode = fileMode;
			super.openAsync(file,fileMode);
		}
		
		public function readFileBuffer():ByteArray{
			lstReadPos.addItem(this.position);
			dataArr = null;
			dataArr = new ByteArray();
			if(this.bytesAvailable > 0){
				this.readBytes(dataArr);
			}
			return dataArr;
		}
		
		private function openFileForWriting():void{
			buffer = new ArrayCollection();
			
			var dataSortField:SortField = new SortField();
			dataSortField.name = "numStartIndex";
			dataSortField.numeric = true;
			dataSortField.descending = false;
			
			var sort:Sort = new Sort();
			sort.fields = [dataSortField];
			
			buffer.sort = sort;
			waitForPcktTimer = new Timer(5000,1);
		}
		
		public function writeBytesAt(data:ByteArray,position:Number):void{
			lstRecvdPos.addItem(position);
			if(this.position == position){
				super.writeBytes(data);
			} else{
				if(buffer.length < 100){
					var objFileDataItem:FileDataItem = new FileDataItem();
					objFileDataItem.fileData = data;
					objFileDataItem.numStartIndex = position;
					buffer.addItem(objFileDataItem);
				} else{
					throw new Error("Buffer size exceeded");
				}
			}
			
			//Check buffer if any stored items can be now written over to the file
			writeBufferItemsToFile();
		}
		
		private function writeBufferItemsToFile():void{
			//Sort the buffer
			buffer.refresh();
			
			for(var index:int = 0; index < buffer.length; index++){
				var objFileDataItem:FileDataItem = buffer.getItemAt(index) as FileDataItem;
				if(objFileDataItem.numStartIndex == this.position){ 
					super.writeBytes(objFileDataItem.fileData as ByteArray);
					buffer.removeItemAt(index);
					index--;
				}
			}
		}
		
		public function closeStream(lstReadPos:ArrayCollection = null):void{
			if((this.fileMode == FileMode.APPEND) || (this.fileMode == FileMode.WRITE) || (this.fileMode == FileMode.UPDATE)){
				if(lstReadPos){
					this.lstReadPos = lstReadPos;
					
					for(var index:int = 0;index < lstRecvdPos.length; index++){
						var indexItem:int = lstReadPos.getItemIndex(lstRecvdPos.getItemAt(index));
						if(indexItem != -1){
							lstReadPos.removeItemAt(indexItem);
						}
					}
				
					writeBufferItemsToFile();
					
					if((lstReadPos.length != 0) || ((numFileSize != 0) && (this.position != numFileSize))){
						//wait for the rerst of the peices to arrive - grace time 5 secs
						waitForPcktTimer.start();
					} else{
						super.close();
						this.dispatchEvent(new FileEvent(FileEvent.FILE_WRITE_COMPLETE));
					}
				} else{
					super.close();
					this.dispatchEvent(new FileEvent(FileEvent.FILE_WRITE_COMPLETE));
				}
			} else{
				super.close();
				this.dispatchEvent(new FileEvent(FileEvent.FILE_WRITE_COMPLETE));
			}
		}
		
		private function fileStreamClosed(event:Event):void{
			file.deleteFile();
			throw new Error("Error writing file. File data missing.");
		}
		
		private function waitComplete(event:TimerEvent):void{
			if(lstReadPos && (lstReadPos.length > 0)){
				//Dispatch an event telling the sender to resend the missing pieces 
				this.dispatchEvent(new FileEvent(FileEvent.FILE_WRITE_INCOMPLETE,lstReadPos));
			} else{
				closeStream();
			}
		}
		
		public override function writeBytes(bytes:ByteArray, offset:uint=0, length:uint=0):void{
			if(lstReadPos){
				var index:int = lstReadPos.getItemIndex(this.position);
				if(index != -1){
					lstReadPos.removeItemAt(lstReadPos.getItemIndex(this.position));
				}
				super.writeBytes(bytes, offset, length);
				
				if(lstReadPos.length == 0){
					super.close();
					this.dispatchEvent(new FileEvent(FileEvent.FILE_WRITE_COMPLETE));
				}
			} else{
				super.writeBytes(bytes, offset, length);
			}
		}
	}
}